--[[
	Lua 阿拉伯数字转中文实现 https://blog.csdn.net/lp12345678910/article/details/121396243
	农历功能复制自 https://github.com/boomker/rime-fast-xhup
--]]

-- 数字转中文：

local M = {}
local lunarGz = require("lunar_translator.lunarGz")

local numerical_units =
	{ "", "十", "百", "千", "万", "十", "百", "千", "亿", "十", "百", "千", "兆", "十", "百", "千" }

local numerical_names = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九" }

function M.convert_arab_to_chinese(number)
	local n_number = tonumber(number)
	assert(n_number, "传入参数非正确number类型!")

	-- 0 ~ 9
	if n_number < 10 then
		return numerical_names[n_number + 1]
	end
	-- 一十九 => 十九
	if n_number < 20 then
		local digit = string.sub(n_number, 2, 2)
		if digit == "0" then
			return "十"
		else
			return "十" .. numerical_names[digit + 1]
		end
	end

	--[[
        1. 最大输入9位
            超过9位，string的len加2位（因为有.0的两位）
            零 ~ 九亿九千九百九十九万九千九百九十九
            0 ~ 999999999
        2. 最大输入14位（超过14位会四舍五入）
            零 ~ 九十九兆九千九百九十九亿九千九百九十九万九千九百九十九万
            0 ~ 99999999999999
    --]]
	local len_max = 9
	local len_number = string.len(number)
	assert(
		len_number > 0 and len_number <= len_max,
		"传入参数位数" .. len_number .. "必须在(0, " .. len_max .. "]之间！"
	)

	-- 01，数字转成表结构存储
	local numerical_tbl = {}
	for i = 1, len_number do
		numerical_tbl[i] = tonumber(string.sub(n_number, i, i))
	end

	local pre_zero = false
	local result = ""
	for index, digit in ipairs(numerical_tbl) do
		local curr_unit = numerical_units[len_number - index + 1]
		local curr_name = numerical_names[digit + 1]
		if digit == 0 then
			if not pre_zero then
				result = result .. curr_name
			end
			pre_zero = true
		else
			result = result .. curr_name .. curr_unit
			pre_zero = false
		end
	end
	result = string.gsub(result, "零+$", "")
	return result
end
function CnDate_translator(y)
	local t, cstr, t2, t1
	cstr = { "〇", "一", "二", "三", "四", "五", "六", "七", "八", "九" }
	t = ""
	t1 = tostring(y)
	if t1.len(tostring(t1)) ~= 8 then
		return t1
	end
	for i = 1, t1.len(t1) do
		t2 = cstr[tonumber(t1.sub(t1, i, i)) + 1]
		if i == 5 and t2 ~= "〇" then
			t2 = "年十"
		elseif i == 5 and t2 == "〇" then
			t2 = "年"
		end
		if i == 6 and t2 ~= "〇" then
			t2 = t2 .. "月"
		elseif i == 6 and t2 == "〇" then
			t2 = "月"
		end
		--if t.sub(t,t.len(t)-1)=="年" then t2=t2 .. "月" end
		if i == 7 and tonumber(t1.sub(t1, 7, 7)) > 1 then
			t2 = t2 .. "十"
		elseif i == 7 and t2 == "〇" then
			t2 = ""
		elseif i == 7 and tonumber(t1.sub(t1, 7, 7)) == 1 then
			t2 = "十"
		end
		if i == 8 and t2 ~= "〇" then
			t2 = t2 .. "日"
		elseif i == 8 and t2 == "〇" then
			t2 = "日"
		end
		t = t .. t2
	end
	return t
end

-- stylua: ignore start

-- 农历：

-- 天干名称
local cTianGan = { "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" }

-- 地支名称
local cDiZhi = { "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" }

-- 属相名称
local cShuXiang = { "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪" }

-- 农历日期名
local cDayName = {
	"初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十",
	"十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
	"廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十",
}

-- 农历月份名
local cMonName = { "正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "冬月", "腊月" }

-- 农历数据
local lunarData = {
    "AB500D2","4BD0883","4AE00DB","A5700D0","54D0581","D2600D8","D9500CC","655147D","56A00D5","9AD00CA","55D027A","4AE00D2",
    "A5B0682","A4D00DA","D2500CE","D25157E","B5500D6","56A00CC","ADA027B","95B00D3","49717C9","49B00DC","A4B00D0","B4B0580",
    "6A500D8","6D400CD","AB5147C","2B600D5","95700CA","52F027B","49700D2","6560682","D4A00D9","EA500CE","6A9157E","5AD00D6",
    "2B600CC","86E137C","92E00D3","C8D1783","C9500DB","D4A00D0","D8A167F","B5500D7","56A00CD","A5B147D","25D00D5","92D00CA",
    "D2B027A","A9500D2","B550781","6CA00D9","B5500CE","535157F","4DA00D6","A5B00CB","457037C","52B00D4","A9A0883","E9500DA",
    "6AA00D0","AEA0680","AB500D7","4B600CD","AAE047D","A5700D5","52600CA","F260379","D9500D1","5B50782","56A00D9","96D00CE",
    "4DD057F","4AD00D7","A4D00CB","D4D047B","D2500D3","D550883","B5400DA","B6A00CF","95A1680","95B00D8","49B00CD","A97047D",
    "A4B00D5","B270ACA","6A500DC","6D400D1","AF40681","AB600D9","93700CE","4AF057F","49700D7","64B00CC","74A037B","EA500D2",
    "6B50883","5AC00DB","AB600CF","96D0580","92E00D8","C9600CD","D95047C","D4A00D4","DA500C9","755027A","56A00D1","ABB0781",
    "25D00DA","92D00CF","CAB057E","A9500D6","B4A00CB","BAA047B","AD500D2","55D0983","4BA00DB","A5B00D0","5171680","52B00D8",
    "A9300CD","795047D","6AA00D4","AD500C9","5B5027A","4B600D2","96E0681","A4E00D9","D2600CE","EA6057E","D5300D5","5AA00CB",
    "76A037B","96D00D3","4AB0B83","4AD00DB","A4D00D0","D0B1680","D2500D7","D5200CC","DD4057C","B5A00D4","56D00C9","55B027A",
    "49B00D2","A570782","A4B00D9","AA500CE","B25157E","6D200D6","ADA00CA","4B6137B","93700D3","49F08C9","49700DB","64B00D0",
    "68A1680","EA500D7","6AA00CC","A6C147C","AAE00D4","92E00CA","D2E0379","C9600D1","D550781","D4A00D9","DA400CD","5D5057E",
    "56A00D6","A6C00CB","55D047B","52D00D3","A9B0883","A9500DB","B4A00CF","B6A067F","AD500D7","55A00CD","ABA047C","A5A00D4",
    "52B00CA","B27037A","69300D1","7330781","6AA00D9","AD500CE","4B5157E","4B600D6","A5700CB","54E047C","D1600D2","E960882",
    "D5200DA","DAA00CF","6AA167F","56D00D7","4AE00CD","A9D047D","A2D00D4","D1500C9","F250279","D5200D1"
}
-- stylua: ignore end

-- 十进制转二进制
local function Dec2bin(n)
	local t, t1
	local tables = {}
	t = tonumber(n)
	while math.floor(t / 2) >= 1 do
		t1 = t and math.fmod(t, 2)
		if t1 > 0 then
			if #tables > 0 then
				table.insert(tables, 1, 1)
			else
				tables[1] = 1
			end
		else
			if #tables > 0 then
				table.insert(tables, 1, 0)
			else
				tables[1] = 0
			end
		end
		t = math.floor(t / 2)
		if t == 1 then
			if #tables > 0 then
				table.insert(tables, 1, 1)
			else
				tables[1] = 1
			end
		end
	end
	return string.gsub(table.concat(tables), "^[0]+", "")
end

-- 2/10/16进制互转
local function Atoi(x, inPuttype, outputtype)
	local r
	if tonumber(inPuttype) == 2 then
		if tonumber(outputtype) == 10 then -- 2进制-->10进制
			r = tonumber(tostring(x), 2)
			-- elseif tonumber(outputtype) == 16 then -- 2进制-->16进制
			-- 	r = bin2hex(tostring(x))
		end
	elseif tonumber(inPuttype) == 10 then
		if tonumber(outputtype) == 2 then -- 10进制-->2进制
			r = Dec2bin(tonumber(x))
		elseif tonumber(outputtype) == 16 then -- 10进制-->16进制
			r = string.format("%x", x)
		end
	elseif tonumber(inPuttype) == 16 then
		if tonumber(outputtype) == 2 then -- 16进制-->2进制
			r = Dec2bin(tonumber(tostring(x), 16))
		elseif tonumber(outputtype) == 10 then -- 16进制-->10进制
			r = tonumber(tostring(x), 16)
		end
	end
	return r
end

-- 农历16进制数据分解
local function Analyze(Data)
	local rtn1, rtn2, rtn3, rtn4
	rtn1 = Atoi(string.sub(Data, 1, 3), 16, 2)
	if string.len(rtn1) < 12 then
		rtn1 = "0" .. rtn1
	end
	rtn2 = string.sub(Data, 4, 4)
	rtn3 = Atoi(string.sub(Data, 5, 5), 16, 10)
	rtn4 = Atoi(string.sub(Data, -2, -1), 16, 10)
	if string.len(rtn4) == 3 then
		rtn4 = "0" .. Atoi(string.sub(Data, -2, -1), 16, 10)
	end
	-- string.gsub(rtn1, "^[0]*", "")
	return { rtn1, rtn2, rtn3, rtn4 }
end

-- 年天数判断
local function isLeap(y)
	local year = tonumber(y)
	if not year then
		return nil
	end
	if math.fmod(year, 400) ~= 0 and math.fmod(year, 4) == 0 or math.fmod(year, 400) == 0 then
		return 366
	else
		return 365
	end
end

-- 返回当年过了多少天
local function leaveDate(y)
	local day, total
	total = 0
	if isLeap(tonumber(string.sub(y, 1, 4))) > 365 then
		day = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
	else
		day = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
	end
	if tonumber(string.sub(y, 5, 6)) > 1 then
		for i = 1, tonumber(string.sub(y, 5, 6)) - 1 do
			total = total + day[i]
		end
		total = total + tonumber(string.sub(y, 7, 8))
	else
		return tonumber(string.sub(y, 7, 8))
	end
	return tonumber(total)
end

-- 计算日期差，两个8位数日期之间相隔的天数，date2>date1
local function diffDate(date1, date2)
	local n, total
	total = 0
	date1 = tostring(date1)
	date2 = tostring(date2)
	if tonumber(date2) > tonumber(date1) then
		n = tonumber(string.sub(date2, 1, 4)) - tonumber(string.sub(date1, 1, 4))
		if n > 1 then
			for i = 1, n - 1 do
				total = total + isLeap(tonumber(string.sub(date1, 1, 4)) + i)
			end
			total = total
				+ leaveDate(tonumber(string.sub(date2, 1, 8)))
				+ isLeap(tonumber(string.sub(date1, 1, 4)))
				- leaveDate(tonumber(string.sub(date1, 1, 8)))
		elseif n == 1 then
			total = isLeap(tonumber(string.sub(date1, 1, 4)))
				- leaveDate(tonumber(string.sub(date1, 1, 8)))
				+ leaveDate(tonumber(string.sub(date2, 1, 8)))
		else
			total = leaveDate(tonumber(string.sub(date2, 1, 8))) - leaveDate(tonumber(string.sub(date1, 1, 8)))
			-- print(date1 .. "-" .. date2)
		end
	elseif tonumber(date2) == tonumber(date1) then
		return 0
	else
		return -1
	end
	return total
end

--Date日期参数格式YYMMDD，dayCount累加的天数
--返回值：公历日期

local function GettotalDay(Date, dayCount)
	local Year, Month, Day, days, total, t
	Date = tostring(Date)
	Year = tonumber(Date.sub(Date, 1, 4))
	Month = tonumber(Date.sub(Date, 5, 6))
	Day = tonumber(Date.sub(Date, 7, 8))
	if isLeap(Year) > 365 then
		days = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
	else
		days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
	end
	if dayCount > days[Month] - Day then
		total = dayCount - days[Month] + Day
		Month = Month + 1
		if Month > 12 then
			Month = Month - 12
			Year = Year + 1
		end
		for i = Month, 12 + Month do
			if isLeap(Year) > 365 then
				days = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
			else
				days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
			end
			if i > 11 then
				t = i - 12
			else
				t = i
			end
			if total > days[t + 1] then
				total = total - days[Month]
				Month = Month + 1
				if Month > 12 then
					Month = Month - 12
					Year = Year + 1
				end
			else
				break
			end
		end
	else
		total = Day + dayCount
	end
	return Year .. "年" .. Month .. "月" .. total .. "日"
end

-- 公历转农历，支持转化范围公元1900-2100年
-- 公历日期 Gregorian:格式 YYYYMMDD
-- <返回值>农历日期 中文 天干地支属相
function M.Date2LunarDate(Gregorian)
	Gregorian = tostring(Gregorian)
	local Year, Month, Day, Pos, Data0, Data1, MonthInfo, LeapInfo, Leap, Newyear, LYear, thisMonthInfo
	Year = tonumber(Gregorian.sub(Gregorian, 1, 4))
	Month = tonumber(Gregorian.sub(Gregorian, 5, 6))
	Day = tonumber(Gregorian.sub(Gregorian, 7, 8))
	if Year > 2100 or Year < 1899 or Month > 12 or Month < 1 or Day < 1 or Day > 31 or string.len(Gregorian) < 8 then
		return "无效日期", "无效日期"
	end

	-- 获取两百年内的农历数据
	Pos = Year - 1900 + 2
	Data0 = lunarData[Pos - 1]
	Data1 = lunarData[Pos]
	-- 判断农历年份
	local tb1 = Analyze(Data1)
	MonthInfo = tb1[1]
	LeapInfo = tb1[2]
	Leap = tb1[3]
	Newyear = tb1[4]
	local Date1 = Year .. Newyear
	local Date2 = Gregorian
	local Date3 = diffDate(Date1, Date2) -- 和当年农历新年相差的天数
	if Date3 < 0 then
		tb1 = Analyze(Data0)
		Year = Year - 1
		MonthInfo = tb1[1]
		LeapInfo = tb1[2]
		Leap = tb1[3]
		Newyear = tb1[4]
		Date1 = Year .. Newyear
		Date2 = Gregorian
		Date3 = diffDate(Date1, Date2)
	end

	Date3 = Date3 + 1
	LYear = Year -- 农历年份，就是上面计算后的值
	if Leap > 0 then -- 有闰月
		thisMonthInfo = string.sub(MonthInfo, 1, tonumber(Leap)) .. LeapInfo .. string.sub(MonthInfo, Leap + 1)
	else
		thisMonthInfo = MonthInfo
	end

	local thisMonth, thisDays, LMonth, LDay, Isleap, LunarDate, LunarDate2, LunarYear, LunarMonth
	for i = 1, 13 do
		thisMonth = string.sub(thisMonthInfo, i, i)
		thisDays = 29 + thisMonth
		if Date3 > thisDays then
			Date3 = Date3 - thisDays
		else
			if Leap > 0 then
				if Leap >= i then
					LMonth = i
					Isleap = 0
				else
					LMonth = i - 1
					if i - Leap == 1 then
						Isleap = 1
					else
						Isleap = 0
					end
				end
			else
				LMonth = i
				Isleap = 0
			end
			LDay = math.floor(Date3)
			break
		end
	end

	if Isleap > 0 then
		LunarMonth = "闰" .. cMonName[LMonth]
	else
		LunarMonth = cMonName[LMonth]
	end

	local _nis = tostring(LYear)
	local _LunarYears = ""
	for i = 1, _nis:len() do
		local _ni_digit = tonumber(_nis:sub(i, i))
		_LunarYears = _LunarYears .. M.convert_arab_to_chinese(_ni_digit)
	end

	LunarYear = string.gsub(_LunarYears, "零", "〇")
	LunarDate = cTianGan[math.fmod(LYear - 4, 10) + 1]
		.. cDiZhi[math.fmod(LYear - 4, 12) + 1]
		.. "年（"
		.. cShuXiang[math.fmod(LYear - 4, 12) + 1]
		.. "）"
		.. LunarMonth
		.. cDayName[LDay]

	LunarDate2 = LunarYear .. "年" .. LunarMonth .. cDayName[LDay]
	return LunarDate, LunarDate2
end

--农历转公历
--农历 Gregorian:数字格式 YYYYMMDD
--<返回值>公历日期 格式YYYY年MM月DD日
--农历日期月份为闰月需指定参数IsLeap为1，非闰月需指定参数IsLeap为0
function M.LunarDate2Date(Gregorian, IsLeap)
	Gregorian = tostring(Gregorian)
	local Year, Month, Day, Pos, Data, MonthInfo, LeapInfo, Leap, Newyear, Sum, thisMonthInfo, GDate
	Year = tonumber(Gregorian.sub(Gregorian, 1, 4))
	Month = tonumber(Gregorian.sub(Gregorian, 5, 6))
	Day = tonumber(Gregorian.sub(Gregorian, 7, 8))
	if Year > 2100 or Year < 1900 or Month > 12 or Month < 1 or Day > 30 or Day < 1 or string.len(Gregorian) < 8 then
		return "无效日期"
	end

	--获取当年农历数据
	Pos = (Year - 1899) + 1
	Data = lunarData[Pos]
	--判断公历日期
	local tb1 = Analyze(Data)
	MonthInfo = tb1[1]
	LeapInfo = tb1[2]
	Leap = tb1[3]
	Newyear = tb1[4]
	--计算到当天到当年农历新年的天数
	Sum = 0

	if Leap > 0 then --有闰月
		thisMonthInfo = string.sub(MonthInfo, 1, tonumber(Leap)) .. LeapInfo .. string.sub(MonthInfo, Leap + 1)
		if Leap ~= Month and tonumber(IsLeap) == 1 then
			return "该月不是闰月！"
		end
		if Month <= Leap and tonumber(IsLeap) == 0 then
			for i = 1, Month - 1 do
				Sum = Sum + 29 + string.sub(thisMonthInfo, i, i)
			end
		else
			for i = 1, Month do
				Sum = Sum + 29 + string.sub(thisMonthInfo, i, i)
			end
		end
	else
		if tonumber(IsLeap) == 1 then
			return "该年没有闰月！"
		end
		for i = 1, Month - 1 do
			thisMonthInfo = MonthInfo
			Sum = Sum + 29 + string.sub(thisMonthInfo, i, i)
		end
	end
	Sum = math.floor(Sum + Day - 1)
	GDate = Year .. Newyear
	GDate = GettotalDay(GDate, Sum)

	return GDate
end

function M.QueryLunarInfo(date)
	local str, LunarDate, LunarGz, result, DateTime
	date = tostring(date)
	result = {}
	str = date:gsub("^(%u+)", "")
	if string.match(str, "^(20)%d%d+$") ~= nil or string.match(str, "^(19)%d%d+$") ~= nil then
		if string.len(str) == 4 then
			str = str .. string.sub(tostring(os.date("%m%d%H")), 1)
		elseif string.len(str) == 5 then
			str = str .. string.sub(tostring(os.date("%m%d%H")), 2)
		elseif string.len(str) == 6 then
			str = str .. string.sub(tostring(os.date("%m%d%H")), 3)
		elseif string.len(str) == 7 then
			str = str .. string.sub(tostring(os.date("%m%d%H")), 4)
		elseif string.len(str) == 8 then
			str = str .. string.sub(tostring(os.date("%m%d%H")), 5)
		elseif string.len(str) == 9 then
			str = str .. string.sub(tostring(os.date("%m%d%H")), 6)
		else
			str = string.sub(str, 1, 10)
		end
		if
			tonumber(string.sub(str, 5, 6)) > 12
			or tonumber(string.sub(str, 5, 6)) < 1
			or tonumber(string.sub(str, 7, 8)) > 31
			or tonumber(string.sub(str, 7, 8)) < 1
			or tonumber(string.sub(str, 9, 10)) > 24
		then
			return result
		end
		LunarDate = M.Date2LunarDate(str)
		LunarGz = lunarGz.LunarGzl(str)
		DateTime = M.LunarDate2Date(str, 0)
		if LunarGz ~= nil then
			result = {
				{ CnDate_translator(string.sub(str, 1, 8)), "〔中文日期〕" },
				{ LunarDate, "〔公历⇉农历〕" },
				{ LunarGz, "〔公历⇉干支〕" },
			}
			if tonumber(string.sub(str, 7, 8)) < 31 then
				table.insert(result, { DateTime, "〔农历⇉公历〕" })
				local leapDate = { M.LunarDate2Date(str, 1) .. "（闰）", "〔农历⇉公历〕" }
				if string.match(leapDate[1], "^(%d+)") ~= nil then
					table.insert(result, leapDate)
				end
			end
		end
	end

	return result
end

return M
